/************************************************************************/
// Copyright (C) 2016, han_gangbiao. All rights reserved.
// Use of this source code is governed by a GPL-v2 license that can be found in the License file.
// 
// [Time]:      2016-1-4 21:53:17
// [Author]:    han_gangbiao [http://my.oschina.net/jackhen]
// [Info]:      
/************************************************************************/

#include "net_binary_decoder.h"

#define MY_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define NET_BINARY_BUF_LEN  (128*1024)     //decoder buffer = 128KB

NetBinaryDecoder::NetBinaryDecoder( IBinaryDecoderListener* listener )
    :m_listener(listener)
{
    m_dataLen = 0;
    m_buffer = new char[NET_BINARY_BUF_LEN];
    memset(m_buffer, 0, NET_BINARY_BUF_LEN);

    m_tmpPdu = NULL;
    m_body = NULL;
    m_bodyLen = 0;
}

NetBinaryDecoder::~NetBinaryDecoder()
{
    m_listener = NULL;

    SAFE_ARRAY_DELETE(m_buffer);
    m_dataLen = 0;

    SAFE_ARRAY_DELETE(m_body);
    m_bodyLen = 0;
}

int NetBinaryDecoder::Parse( int fd, const char* data, int len )
{
    if(m_tmpPdu != NULL) {
        int contentLen = GetContentLength(m_tmpPdu);
        int copyLen = MY_MIN(contentLen - m_bodyLen, len);
        memcpy(m_body+m_bodyLen, data, copyLen);
        m_bodyLen += copyLen;

        if(m_bodyLen == contentLen) {
            m_body[m_bodyLen] = 0;
            m_tmpPdu->setBody(m_body, m_bodyLen);
            if(m_listener)
                m_listener->onPDU(fd, m_tmpPdu);
 
            m_tmpPdu = NULL;
            SAFE_ARRAY_DELETE(m_body);
            m_bodyLen = 0;

            if(len - copyLen > 0) {
                data += copyLen;
                len -= copyLen;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }

    if(m_dataLen + len >= NET_BINARY_BUF_LEN) { 
        if(m_listener)
            m_listener->onError(fd, BINARY_DECODER_ERROR_BUFFER_FULL);
        m_dataLen = 0;
    }

    if(m_dataLen < 0) {
        //should not be found
        if(m_listener)
            m_listener->onError(fd, BINARY_DECODER_ERROR_UNKNOWN);
        m_dataLen = 0;
    }

    memcpy(m_buffer+m_dataLen, data, len);
    m_dataLen += len;

    m_buffer[m_dataLen] = 0;

    char* buf = m_buffer;
    int remainLen = m_dataLen;
    int pduLen = 0;
    smart::ref_ptr<NetBinaryPdu> pdu = NULL;
    while(remainLen > 0) {
        pdu = NetBinaryPdu::createPduFromStream(buf, remainLen, pduLen);
        if(pdu == NULL) {
            if(pduLen < 0) { //fix @2016-10-12 10:05:13
                //if(m_listener) m_listener->onError(fd, BINARY_DECODER_ERROR_PACKET_LACK); //maybe len < head_len, not error!
            } else {
                if(m_listener) m_listener->onError(fd, BINARY_DECODER_ERROR_PARSE_FAILED);
            }
            break;
        }

        remainLen -= pduLen;
        buf += pduLen;

        int contentLen = GetContentLength(pdu);
        if(contentLen >= NET_BINARY_BUF_LEN/2 || contentLen > pdu->getBodyLen()) {
            m_tmpPdu = pdu;
            if(NULL != m_body) {
                delete [] m_body;
                m_body = NULL;
            }
            if(NULL == m_body) {
                m_body = new char[contentLen + 1];
                memset(m_body, 0, contentLen+1);
            }

            const char* binBody = pdu->getBody();
            m_bodyLen = pdu->getBodyLen();
            memcpy(m_body, binBody, m_bodyLen);

            if(remainLen > 0) {
                int copyLen = MY_MIN(contentLen-m_bodyLen, remainLen);
                memcpy(m_body+m_bodyLen, buf, copyLen);
                m_bodyLen += copyLen;

                remainLen -= copyLen;
                buf += copyLen;

                if(m_bodyLen == contentLen) {
                    m_body[m_bodyLen] = 0;
                    pdu->setBody(m_body, m_bodyLen);
                    if(m_listener)
                        m_listener->onPDU(fd, m_tmpPdu);

                    m_tmpPdu = NULL;
                    SAFE_ARRAY_DELETE(m_body);
                    m_bodyLen = 0;
                }
            }
        } else {
            if(m_listener)
                m_listener->onPDU(fd, pdu);
            pdu = NULL;
        }
    }//~while

    if(remainLen > 0) {
        memmove(m_buffer, buf, remainLen);
    }
    m_dataLen = remainLen;

    return 0;
}

int NetBinaryDecoder::GetContentLength( smart::ref_ptr<NetBinaryPdu> pdu ) const
{//body len
    if(pdu)
    {
        return (int)( pdu->getPduLength() - pdu->getHeaderLen() );
    }
    return 0;
}
